﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Common;
using DataAccess;
using System.Web;
using System.Net.Mail;
using System.Net;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net.Mime;
using Common.Views;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp;
using Google.GData.Calendar;
using Google.GData.Client;
using Google.GData.Extensions;

namespace BusinessLogic
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "FaultsService" in code, svc and config file together.
    
    /// <summary>
    /// This service contains methods which send and get data regarding faults to and
    /// from the Faults, FaultDetails, FaultStatus and FaultCategories tables in the database.
    /// </summary>
    public class FaultsService : IFaultsService
    {
        /// <summary>
        /// A method which retrieves the status of the fault
        /// </summary>
        /// <param name="orderID">The ID of the order for which to retreive the product</param>
        /// <param name="productID">The ID of the product for which to retreive the fault status</param>
        /// <returns>A string value which represents the fault status. If there is not fault status, null is returned</returns>
        public string GetFaultsForOrder(Guid orderID, int productID)
        {
            return new FaultsRepository().GetFaultsForOrder(orderID, productID);
        }

        /// <summary>
        /// A method which retrieves a list of all the fault categories
        /// </summary>
        /// <returns>A list of fault categories</returns>
        public IEnumerable<FaultCategory> GetFaultCategories()
        {
            return new FaultsRepository().GetFaultCategories();
        }

        /// <summary>
        /// A method which adds a new fault to the database regarding a faulty product
        /// </summary>
        /// <param name="faultId">The ID of the fault</param>
        /// <param name="faultCatId">The ID of the fault category</param>
        /// <param name="orderId">The ID of the order</param>
        /// <param name="productId">The ID of the product to report</param>
        /// <param name="details">The details of the fault</param>
        /// <param name="barcode">The barcode which was previously generated using a web service</param>
        /// <param name="username">The username of the user who is reporting a faulty product</param>
        public void AddFault(Guid faultId, int faultCatId, Guid orderId, int productId, string details, byte[] barcode, string username)
        {
            Fault f = new Fault();
            f.FaultID = faultId;
            f.FaultCategoryID = faultCatId;
            f.OrderID = orderId;
            f.ProductID = productId;

            FaultDetail fd = new FaultDetail();
            fd.FaultID = faultId;
            fd.Date = DateTime.Now;
            fd.Details = details;
            fd.FaultStatusID = 1; //Reported
            
            FaultsRepository fr = new FaultsRepository();
            UsersRepository ur = new UsersRepository();
            string faultStatus = fr.GetFaultStatus(fd.FaultStatusID);
            fr.Entity = ur.Entity;

            try
            {
                fr.Entity.Connection.Open();
                fr.Transaction = ur.Transaction = fr.Entity.Connection.BeginTransaction();

                fr.AddFault(f);
                fr.AddFaultDetails(fd);
                string email = ur.GetUserEmail(username);

                //FileStream fs = new FileStream(faultId + ".pdf", FileMode.Create, FileAccess.Write, FileShare.None); //I think this is to save it
                //Creating the Image from the barcode
                MemoryStream ms = new MemoryStream(); //This is just to save the file to memory, since it will be sent over an email
                ImageConverter ic = new ImageConverter();
                System.Drawing.Image imgBarcode = (System.Drawing.Image)ic.ConvertFrom(barcode);
                imgBarcode.Save(ms, ImageFormat.Jpeg);
                ms.Position = 0;
                ContentType ct = new ContentType();
                ct.MediaType = MediaTypeNames.Image.Jpeg;
                ct.Name = "Barcode - "+faultId.ToString();

                //Creating the PDF with details
                MemoryStream ms2 = new MemoryStream();
                Document doc = new Document();
                PdfWriter writer = PdfWriter.GetInstance(doc, ms2);
                doc.Open();

                PdfPTable table = new PdfPTable(5);
                PdfPCell header = new PdfPCell(new Phrase("Fault Details"));
                header.Colspan = 5;
                header.HorizontalAlignment = 1;
                header.Border = 0; 
                table.AddCell(header);

                table.AddCell("Fault ID");
                table.AddCell("Product ID");
                table.AddCell("Date");
                table.AddCell("Fault Details");
                table.AddCell("Fault Status");

                table.AddCell(faultId.ToString());
                table.AddCell(productId.ToString());
                table.AddCell(DateTime.Now.ToShortDateString());
                table.AddCell(details);
                table.AddCell(faultStatus);

                doc.Add(table);
                writer.CloseStream = false;
                doc.Close();
                ms2.Position = 0;

                //Send email to the user with the following details: barcode, product id, fault details
                MailMessage mm = new MailMessage();
                mm.To.Add(email);
                mm.From = new MailAddress("vanityclothing01@gmail.com", "Electros Ltd");
                mm.Subject = "Fault Receipt";
                mm.Body = "Dear user, <br/><br/>Thank you for reporting a faulty product.<br/>";
                mm.Body += "Attached please find the fault details together with the barcode.<br/><br/>";
                mm.Body += "Yours sincerely, <br/> <b>Electros Ltd</b>";
                //Adding the image and the pdf as attachments
                mm.Attachments.Add(new Attachment(ms, ct));
                mm.Attachments.Add(new Attachment(ms2, faultId+".pdf"));
                mm.IsBodyHtml = true;
                
                SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
                client.EnableSsl = true;
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                client.Credentials = new NetworkCredential("vanityclothing01@gmail.com", "vanityadmin");
                client.Send(mm);

                fr.Transaction.Commit();
            }
            catch (Exception ex)
            {
                fr.Transaction.Rollback();
            }
            finally
            {
                fr.Entity.Connection.Close();
            }
        }

        /// <summary>
        /// A method which gets a list of transactions for a particular username, for a chosen product, between the chosen dates
        /// </summary>
        /// <param name="start">The start date chosen by the user</param>
        /// <param name="end">The end date chosen by the user</param>
        /// <param name="productId">The ID of the product chosen</param>
        /// <param name="username">The username for whom to get the transactions</param>
        /// <returns>A list of transactions</returns>
        public IEnumerable<OrdersView> FilterTransactions(DateTime start, DateTime end, int productId, string username)
        {
            return new FaultsRepository().FilterTransactions(start, end, productId, username);
        }

        /// <summary>
        /// A method used to get all the faults
        /// </summary>
        /// <returns>A list of all the faults made by all users</returns>
        public IEnumerable<Fault> GetAllFaults()
        {
            return new FaultsRepository().GetAllFaults();
        }

        /// <summary>
        /// A method which gets log entries according to the latest first
        /// </summary>
        /// <returns>A list of log entries sorted by date</returns>
        public IEnumerable<FaultsView> SortEntriesByDate()
        {
            return new FaultsRepository().SortEntriesByDate();
        }

        /// <summary>
        /// A method which gets log entries between certain dates
        /// </summary>
        /// <param name="start">The start date from when to retrieve the log entry</param>
        /// <param name="end">The end date to when to retrieve the log entry</param>
        /// <returns>A list of log entries between two dates</returns>
        public IEnumerable<FaultsView> FilterByLogDate(DateTime start, DateTime end)
        {
            return new FaultsRepository().FilterByLogDate(start, end);
        }

        /// <summary>
        /// A method which gets log entries according to the fault ID
        /// </summary>
        /// <param name="faultId">The ID of the fault</param>
        /// <returns>A list of log entries retrieved according to the fault ID</returns>
        public IEnumerable<FaultsView> FilterByFaultID(Guid faultId)
        {
            return new FaultsRepository().FilterByFaultID(faultId);
        }

        /// <summary>
        /// A method which gets a log entries according to the client ID
        /// </summary>
        /// <param name="username">The ID of the client - username</param>
        /// <returns>A list of log entries for a particular user</returns>
        public IEnumerable<FaultsView> FilterByClientID(string username)
        {
            return new FaultsRepository().FilterByClientID(username);
        }

        /// <summary>
        /// A method which gets log entries according to a particular 
        /// client ID, fault ID and between two specific dates
        /// </summary>
        /// <param name="faultId">The ID of the fault for which to get the log entries</param>
        /// <param name="username">The username of the client for whom to get the log entries</param>
        /// <param name="start">The start date from when to retrieve the log entries</param>
        /// <param name="end">The end date to when to retrieve the log entries</param>
        /// <returns>A list of log entries which is filtered by the fault ID, 
        /// client ID, which is between two specified dates</returns>
        public IEnumerable<FaultsView> FilterAll(Guid faultId, string username, DateTime start, DateTime end)
        {
            return new FaultsRepository().FilterAll(faultId, username, start, end);
        }

        /// <summary>
        /// A method which gets all the log entries found in the database
        /// </summary>
        /// <returns>A list of all the log entries</returns>
        public IEnumerable<FaultsView> GetAllLogEntires()
        {
            return new FaultsRepository().GetAllLogEntires();
        }

        /// <summary>
        /// A method which gets all the fault statuses available in the database
        /// </summary>
        /// <returns>A list of fault statuses</returns>
        public IEnumerable<FaultStatu> GetAllFaultStatuses()
        {
            return new FaultsRepository().GetAllFaultStatuses();
        }

        /// <summary>
        /// A method which adds a new log entry to the database when the status of the fault for a reported product has been updated.
        /// This method also sends an SMS to the user to inform them about the update. An e-mail is also sent to the user with the update.
        /// </summary>
        /// <param name="OrderID">The ID of the order which is used to retrieve the user's email address and mobile number</param>
        /// <param name="faultID">The ID of the fault to update</param>
        /// <param name="details">The details of the fault</param>
        /// <param name="statusID">The new ID of the fault status</param>
        public void UpdateFault(Guid OrderID, Guid faultID, string details, int statusID)
        {
            FaultDetail fd = new FaultDetail();
            fd.FaultID = faultID;
            fd.Date = DateTime.Now;
            fd.Details = details;
            fd.FaultStatusID = statusID;

            FaultsRepository fr = new FaultsRepository();
            UsersRepository ur = new UsersRepository();

            string faultStatus = fr.GetFaultStatus(fd.FaultStatusID);

            fr.Entity = ur.Entity;
            try
            {
                fr.Entity.Connection.Open();
                fr.Transaction = ur.Transaction = fr.Entity.Connection.BeginTransaction();
                fr.AddFaultDetails(fd);

                string email = ur.GetUserEmailByOrder(OrderID);
                string mobileNo = ur.GetUserNumberByOrder(OrderID);

                //Email is sent to the user to inform them that the fault status has been updated
                MailMessage mm = new MailMessage();
                mm.To.Add(email);
                mm.From = new MailAddress("vanityclothing01@gmail.com", "Electros Ltd");
                mm.Subject = "Fault Status Update";
                mm.Body = "Dear User, <br/><br/>The status of the fault for your reported product has been updated. The fault status is now " + faultStatus + ". <br/>";
                mm.Body += "Yours sincerely, <br/> <b>Electros Ltd</b>";
                mm.IsBodyHtml = true;

                SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
                client.EnableSsl = true;
                client.DeliveryMethod = SmtpDeliveryMethod.Network;
                client.Credentials = new NetworkCredential("vanityclothing01@gmail.com", "vanityadmin");
                client.Send(mm);

                //An SMS is sent to the user to inform them that the fault status has been updated
                string apiURL = "http://api.clickatell.com/http/sendmsg?user=iJanica&password=FDXAFJPIEgSRZJ&api_id=3477212&to=" + mobileNo + "&text=Your+Fault+Status+has+been+Updated+to+"+faultStatus+"";
                Uri Address = new Uri(apiURL);
                HttpWebRequest request = WebRequest.Create(Address) as HttpWebRequest;
                request.Method = "GET";
                request.ContentType = "text/xml";
                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream());
                    string strOutputXml = reader.ReadToEnd();
                }

                //Google API Code
                CalendarService service = new CalendarService("ElectrosLtdApplication");
                service.setUserCredentials("vanityclothing01@gmail.com", "vanityadmin");
                
                EventEntry entry = new EventEntry();
                entry.Title.Text = "Fault Status Update";
                entry.Content.Content = "The status of the fault for your reported product has been updated. The fault status is now " + faultStatus;
                Where eventLocation = new Where();
                eventLocation.ValueString = "Electros Ltd";
                entry.Locations.Add(eventLocation);
                When eventTime = new When(DateTime.Now, DateTime.Now.AddHours(2));
                entry.Times.Add(eventTime);
                Who guest = new Who();
                guest.Email = email;
                guest.Rel = "EVENT_ATTENDEE";
                entry.Participants.Add(guest);
                
                Uri postUri = new Uri("https://www.google.com/calendar/feeds/default/private/full");
                AtomEntry insertedEntry = service.Insert(postUri, entry);

                fr.Transaction.Commit();
            }
            catch (Exception ex)
            {
                fr.Transaction.Rollback();
            }
            finally
            {
                fr.Entity.Connection.Close();
            }
        }
    }
}
